// let const var 声明变量的区别
export module ConstVarLet {
    // 1,函数外 var 声明的变量会挂载到window上去，相当于是全局的
    let stra: String = 'l: 您好啊 leeneo';
    const strb: String = 'c: 您好啊'
    var strc: String = 'v: 您好啊'

    // console.log(window.stra);
    // console.log(window.strb);
    // console.log(window.strc);     // 这行在node里无法运行，用浏览器可以

    // 2,let,var 可以先声明再赋值，const 必需声明的同时赋值，并且不能直接修改const的值
    // 如果想改变const声明的变量，那就把它声明为一个内存地址（也就是对象），对象内部的值是可以随意修改的
    var a;
    let b;
    // const c;     
    console.log(a); // undefined
    console.log(b); // undefined
    // console.log(c);

    // 3,作用域上面 var是全局的 let和const则是块级作用域的
    {
        var bla = 666;
        let blb = 222;
        const blc = 111;
    }
    console.log('代码块外 var bla: ' + bla)
    // console.log('代码块外 let blb: ' + blb)  //error not defined
    // console.log('代码块外 const blc: ' + blc)   //error not defined

    if (true) {
        var blocka = 33;
        let blockb = 22;
        const blockc = 11;
        console.log('代码块内 var' + blocka)
        console.log('代码块内 let' + blockb)
        console.log('代码块内 const' + blockc)
    }
    console.log('代码块外 var' + blocka)        // 也就是说在另一个文件内也可以访问var 声明的变量
    // console.log('代码块外 let'+blockb)       // 报错，not defined
    // console.log('代码块外 const'+blockc)     // 报错, not defined

    // 使用 window 访问
    // 当前let 作用域仅在for内有效
    for (let j = 0; j < 1; j++) {
        let ltA = '123';
        var varA = 'test For val'
        // console.log('window for 内 ltA: ',window.ltA);    //error undefined,let 不会挂载在window上
        console.log('for 块内,ltA: ', ltA);                    //ok
    }
    // console.log('for 外 ltA: ',ltA);      // error undefined
    // console.log('for 外 varA: ',varA);  //ts代码提示提前使用了varA,但js可以正常执行，输出：test for val
    // console.log('for 块外,window.varA: ', window.varA); //可以访问

    if (true) {
        let ltB = 'testX';
        var varB = 'testX';
    }
    // console.log('window if letB: ',window.ltB);     //  error undefined
    // console.log('if 块外,window.varB: ', window.varB);    //  可以访问到




    // 4,相同作用域内，var 变量可以重复声明，let 就不可以了
    var slA = 'test var';
    console.log(slA);
    var slA = 'test same var'
    console.log(slA);

    let lA = 'test let';
    // let lA='testA'   // error: lA has declared 
    console.log(lA);

    // 5,异步函数内调用外部变量的区别
    // 每个setTimeout匿名函数内部的i都是同一个，因为setTimeout函数异步执行，即在for循环结束后执行
    // 所以setTimeout内部的i 永远是10
    for (var i = 0; i < 3; i++) {
        // console.log('var i: ', i);
        setTimeout(function () { console.log('异步 var i: ', i); }, 100);
    }
    //输出3 3次

    // 每个setTimeout 匿名函数内部的i都是独立的，因为作用域原因，for不能影响匿名函数内的i
    for (let i = 0; i < 3; i++) {
        // console.log('let i: ', i);
        setTimeout(function () { console.log('异步 let i: ', i); }, 100);
    }
    //输出0-2

    // 可能上面的作用域不太好理解，那换个方式
    let btns = document.getElementsByClassName('btnForVar');
    let btns2 = document.getElementsByClassName('closureVar');
    let btns3 = document.getElementsByClassName('btnLet');
    for (var k = 0; k < btns.length; k++) {
        // k始终是3，因为相对于function,外部的var一直在变
        btns[k].addEventListener('click', function (e) {
            console.log('第 ' + k + ' 个按钮被点击');
        })
    }

    // 以前解决类似这样的问题方法是使用闭包
    // 因为函数作用域，只在函数内有效，不管是let 还是var
    for (var k = 0; k < btns.length; k++) {
        // k始终是3，因为相对于function,外部的var一直在变
        (function (i) {
            btns2[i].addEventListener('click', function (e) {
                console.log('第 ' + parseInt((i + 1).toString()) + ' 个按钮被点击');
            })
        })(k)
    }

    // 使用let 就可以轻松解决这类问题
    for (let k = 0; k < btns.length; k++) {
        btns3[k].addEventListener('click', function (e) {
            console.log('第 ' + parseInt((k + 1).toString()) + ' 个按钮被点击');
        })
    }
}